/**
 * @author Cator Vee  hi@catorv.com
 */
import { isFunction, mixin, nop, tpl } from '../utils'
import { buildUrl, queryString, urlEncode } from '../utils/url'
import { app, dispatch } from '../'
import ready from './ready'
import { storage } from './data'
import env from '../lib/env'

const STORAGE_KEY = 'wechat-data'
const TYPE_UPDATE = '@@WECHAT_MP/UPDATE'
const NAMESPACE = 'wechat'

const location = window.location
const href = location.href.replace(/#.*$/, '')

const SCENE_FRIEND = 1
const SCENE_TIMELINE = 2
const SCENE_QQ = 3
const SCENE_WEIBO = 4
const SCENE_QZONE = 5

const APIS = [
  'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ',
  'onMenuShareWeibo', 'onMenuShareQZone', 'startRecord', 'stopRecord',
  'onVoiceRecordEnd', 'playVoice', 'pauseVoice', 'stopVoice', 'onVoicePlayEnd',
  'uploadVoice', 'downloadVoice', 'chooseImage', 'previewImage', 'uploadImage',
  'downloadImage', 'translateVoice', 'getNetworkType', 'openLocation',
  'getLocation', 'hideOptionMenu', 'showOptionMenu', 'hideMenuItems',
  'showMenuItems', 'hideAllNonBaseMenuItem', 'showAllNonBaseMenuItem',
  'closeWindow', 'scanQRCode', 'chooseWXPay', 'openProductSpecificView',
  'addCard', 'chooseCard', 'openCard'
]

let inited = false

let txwx

let data
let config
let shareOptions
let mpapi
let params

let wechat = {
  init: function (_params) {
    if (inited) {
      return Promise.resolve()
    }
    inited = true

    mpapi = app.apis.wechat.mp
    if (!data.openid) {
      let query = queryString()
      const {openid, access_token: accessToken, refresh_token: refreshToken, scope} = query
      let url
      if (openid && accessToken && refreshToken && scope) {
        app.updateContext({openid, userInfo: query.userinfo})
        storage.set(STORAGE_KEY, {openid})
        delete query.access_token
        delete query.expires_in
        delete query.refresh_token
        delete query.openid
        delete query.scope
        delete query.userinfo
        url = buildUrl(query)
      } else {
        url = config.oauth.replace('{state}', urlEncode(location.href))
      }
      location.href = url
      return Promise.reject(new Error())
    }

    let jobs = [
      _params ? Promise.resolve(_params) : mpapi.jscfg.doGet({
        url: href
      }),
      data.userInfo ? Promise.resolve(data.userInfo)
        : mpapi.userInfo.doGet({id: data.openid})
    ]
    if (!('wx' in window)) {
      jobs.push(ready(config.jssdk))
    }

    return Promise.all(jobs).then(function (data) {
      params = data[0]
      if (data[2]) {
        // eslint-disable-next-line
        txwx = wx
      }
      return wechatInit()
    })
  },

  setState: function (state) {
    dispatch(TYPE_UPDATE, state)
  },

  update: function (options) {
    shareOptions = mixin({}, options && options.share)
    this.updateShareOptions(shareOptions)
    this.setState({ready: true})
  },

  updateShareOptions: function (options) {
    const defaultOptions = config.share || {}
    ;['default', 'friend', 'timeline', 'qq', 'weibo', 'qzone'].forEach(scene => {
      let value = options[scene]
      if (value) {
        shareOptions[scene] = {...(defaultOptions[scene] || {}), ...value}
      } else if (value === null) {
        delete shareOptions[scene]
      }
    })
    updateShareOptions(shareOptions)
  },

  handlers: {
    success: nop, // function (res, scene, settings) {}
    fail: nop,
    cancel: nop,
    complete: nop
  }
}

APIS.forEach(function (name) {
  wechat[name] = function (options = {}) {
    if (isFunction(options)) {
      options = {
        success: options
      }
    }
    return new Promise(function (resolve, reject) {
      let success = options.success
      let fail = options.fail
      options.success = function (res) {
        success && success(res)
        resolve(res)
      }
      options.fail = function (res) {
        fail && fail(res)
        reject(res)
      }
      if (name in txwx) {
        txwx[name](options)
      } else {
        // v.ui.alert('您的微信版本太低或者部分功能未加载成功，请升级微信客户端或稍后重试。')
        window.alert('您的微信版本太低或者部分功能未加载成功，请升级微信客户端或稍后重试。')
      }
    })
  }
})

function wechatInit () {
  if (!params) {
    return
  }

  txwx.config({
    debug: config.debug,
    appId: data.appId,
    timestamp: params.timestamp,
    nonceStr: params.nonceStr,
    signature: params.signature,
    jsApiList: APIS
  })
  // txwx.error(function(res){
  //   alert(JSON.stringify(res))
  // })

  if (config.debug) {
    console.log('Wechat JSSDK is configured.', params)
  }

  return new Promise(function (resolve) {
    txwx.ready(function () {
      wechat.update()

      if (config.debug) {
        console.log('Wechat JSSDK is ready.')
      }

      resolve()
    })
  })
}

function updateShareOptions (options) {
  function delegateHandler (type, options, callback) {
    return function (res) {
      if (callback) {
        callback(res, options)
      }
      wechat.handlers[type](res, options)
    }
  }

  function getOptions (link, shareOptions) {
    return {
      title: shareOptions.title,
      desc: shareOptions.desc,
      link: link,
      imgUrl: shareOptions.imgUrl,
      success: delegateHandler('success', shareOptions, shareOptions.onSuccess),
      fail: delegateHandler('fail', shareOptions, shareOptions.onFail),
      cancel: delegateHandler('cancel', shareOptions, shareOptions.onCancel),
      complete: delegateHandler('complete', shareOptions, shareOptions.onComplete)
    }
  }

  let optionsFriend = getShareOptions(SCENE_FRIEND, options)
  let optionsTimeline = getShareOptions(SCENE_TIMELINE, options)
  let optionsQQ = getShareOptions(SCENE_QQ, options)
  let optionsWeibo = getShareOptions(SCENE_WEIBO, options)
  let optionsQZone = getShareOptions(SCENE_QZONE, options)

  return Promise.all([
    getShareLink(SCENE_FRIEND, optionsFriend),
    getShareLink(SCENE_TIMELINE, optionsTimeline),
    getShareLink(SCENE_QQ, optionsQQ),
    getShareLink(SCENE_WEIBO, optionsWeibo),
    getShareLink(SCENE_QZONE, optionsQZone)
  ]).then(links => {
    txwx.ready(() => {
      // 2.1 监听“分享给朋友”，自定义分享内容及分享结果接口
      txwx.onMenuShareAppMessage(getOptions(links[0], optionsFriend))
      // 2.2 监听“分享到朋友圈”按钮点击、自定义分享内容及分享结果接口
      txwx.onMenuShareTimeline(getOptions(links[1], optionsTimeline))
      // 2.3 监听“分享到QQ”按钮点击、自定义分享内容及分享结果接口
      txwx.onMenuShareQQ(getOptions(links[2], optionsQQ))
      // 2.4 监听“分享到微博”按钮点击、自定义分享内容及分享结果接口
      txwx.onMenuShareWeibo(getOptions(links[3], optionsWeibo))
      // 2.5 监听“分享到QQ空间”按钮点击、自定义分享内容及分享结果接口
      txwx.onMenuShareQZone(getOptions(links[4], optionsQZone))
    })
  })
}

function getShareLink (scene, options) {
  let shareParam = {
    __uid: data.openid,
    __appid: data.appId,
    __scene: scene
  }

  const query = queryString()
  if (query.cmd || query.fake) {
    if (query.cmd) {
      delete query.cmd
    }
    if (query.fake) {
      delete query.fake
    }
  }

  let link = options.link
  if (link) {
    link = tpl(link, {...shareParam, url: buildUrl(query)}, '')
  } else {
    link = buildUrl(mixin(query, shareParam))
  }
  if (config.autoShortUrl) {
    return mpapi.shortUrl.doGet({url: link}).then(data => data.url)
  } else {
    return Promise.resolve(link)
  }
}

function getShareOptions (scene, shareConfig) {
  const defaultOptions = shareConfig.default || {}
  let options
  switch (scene) {
    case SCENE_FRIEND:
      options = shareConfig.friend
      break
    case SCENE_TIMELINE:
      options = shareConfig.timeline
      break
    case SCENE_QQ:
      options = shareConfig.qq
      break
    case SCENE_WEIBO:
      options = shareConfig.weibo
      break
    case SCENE_QZONE:
      options = shareConfig.qzone
      break
    default:
      options = {}
  }
  return mixin({scene}, options, defaultOptions)
}

window.wechat = wechat
export default wechat

export function enableWechatSupport (opts = {}) {
  config = app.config.wechat || {}
  const namespace = opts.namespace || NAMESPACE
  const initialState = data = mixin({
    ready: false,
    appId: config.appId
  }, storage.get(STORAGE_KEY) || {})
  const extraReducers = {
    [namespace] (state = initialState, {type, payload}) {
      let ret
      switch (type) {
        case TYPE_UPDATE:
          ret = data = {...state, ...payload}
          storage.set(STORAGE_KEY, ret)
          if (payload.openid) {
            app.updateContext({openid: data.openid})
          }
          break
        default:
          ret = state
          break
      }
      return ret
    }
  }

  if (env.wechat) {
    app.preprocessing = app.preprocessing.then(() => wechat.init(null, config))
  }

  return {extraReducers}
}
